package com.boyi.shared.util;

import gnu.io.*;
import org.apache.commons.io.IOUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.io.*;
import java.nio.ByteBuffer;
import java.util.Enumeration;
import java.util.TooManyListenersException;

/**
 * @author 颜培轩
 * @date 2018年07月11日  上午 09:21
 */
public class DSerialPort implements SerialPortEventListener {
    private Logger logger = LoggerFactory.getLogger(DSerialPort.class);
    private CommPortIdentifier commPort;
    private SerialPort serialPort;
    private InputStream inputStream;
    private OutputStream outputStream;

    public DSerialPort(int comId) throws Exception {
        setCommPort(comId);
    }

    /**
     * @方法名称 :openPort
     * @功能描述 :打开SerialPort
     * @返回值类型 :void
     */
    public void open() throws Exception {
        logger.info("端口选择成功，当前端口：" + commPort.getName() + ",现在实例化 SerialPort:");
        try {
            serialPort = (SerialPort) commPort.open("串口通讯", 2000);
            serialPort.setSerialPortParams(115200, SerialPort.DATABITS_8,
                    SerialPort.STOPBITS_1, SerialPort.PARITY_NONE);
            logger.info("实例 SerialPort 成功！");
            startRead();
        } catch (PortInUseException e) {
            throw new Exception(String.format("端口'%1$s'正在使用中！", commPort.getName()), e);
        } catch (UnsupportedCommOperationException e) {
            // TODO Auto-generated catch block
            throw new Exception("打开端口其他异常", e);
        }

    }

    /**
     * @param bytes
     * @方法名称 :write
     * @功能描述 :向端口发送数据，请在调用此方法前 先选择端口，并确定SerialPort正常打开！
     * @返回值类型 :void
     */
    public void write(byte[] bytes) {
        checkPort();

        try {
            outputStream = new BufferedOutputStream(serialPort.getOutputStream());
        } catch (IOException e) {
            throw new RuntimeException("获取端口的OutputStream出错：" + e.getMessage());
        }

        try {
            outputStream.write(bytes);
            logger.info("信息发送成功！");
        } catch (IOException e) {
            throw new RuntimeException("向端口发送信息时出错：" + e.getMessage());
        } finally {
            try {
                outputStream.close();
            } catch (Exception e) {
            }
        }
    }


    /**
     * @方法名称 :close
     * @功能描述 :关闭 SerialPort
     * @返回值类型 :void
     */
    public void close() {
        serialPort.close();
        serialPort = null;
        commPort = null;
    }


    public void setCommPort(int comId) throws Exception {
        String portName = "COM" + comId;
        this.commPort = null;
        CommPortIdentifier cpid;
        Enumeration en = CommPortIdentifier.getPortIdentifiers();

        while (en.hasMoreElements()) {
            cpid = (CommPortIdentifier) en.nextElement();
            if (cpid.getPortType() == CommPortIdentifier.PORT_SERIAL
                    && cpid.getName().equals(portName)) {
                this.commPort = cpid;
                break;
            }
        }
        if (commPort == null) throw new Exception(String.format("无法找到名字为'%1$s'的串口！", portName));
    }


    /**
     * @方法名称 :startRead
     * @功能描述 :开始监听从端口中接收的数据
     * @返回值类型 :void
     */
    private void startRead() {
        try {
            inputStream = new BufferedInputStream(serialPort.getInputStream());
        } catch (IOException e) {
            throw new RuntimeException("获取端口的InputStream出错：" + e.getMessage());
        }

        try {
            serialPort.addEventListener(this);
        } catch (TooManyListenersException e) {
            throw new RuntimeException(e.getMessage());
        }

        serialPort.notifyOnDataAvailable(true);
    }

    /**
     * @方法名称 :checkPort
     * @功能描述 :检查端口是否正确连接
     * @返回值类型 :void
     */
    private void checkPort() {
        if (commPort == null)
            throw new RuntimeException("没有选择端口");

        if (serialPort == null) {
            throw new RuntimeException("SerialPort 对象无效！");
        }
    }

    @Override
    public void serialEvent(SerialPortEvent serialPortEvent) {
        switch (serialPortEvent.getEventType()) {
            case SerialPortEvent.BI:/* Break interrupt,通讯中断 */
            case SerialPortEvent.OE:/* Overrun error，溢位错误 */
            case SerialPortEvent.FE:/* Framing error，传帧错误 */
            case SerialPortEvent.PE:/* Parity error，校验错误 */
            case SerialPortEvent.CD:/* Carrier detect，载波检测 */
            case SerialPortEvent.CTS:/* Clear to send，清除发送 */
            case SerialPortEvent.DSR:/* Data set ready，数据设备就绪 */
            case SerialPortEvent.RI:/* Ring indicator，响铃指示 */
            case SerialPortEvent.OUTPUT_BUFFER_EMPTY:/*
                                                 * Output buffer is
												 * empty，输出缓冲区清空
												 */
                break;
            case SerialPortEvent.DATA_AVAILABLE:/*
                                             * Data available at the serial
											 * port，端口有可用数据。读到缓冲数组，输出到终端
											 */
                byte[] temp = new byte[100];
                int sum = 0;
                ByteBuffer byteBuf = ByteBuffer.allocate(1024);
                try {

                    while (inputStream.available() > 0) {
                        int len = IOUtils.read(inputStream, temp);
                        byteBuf.put(temp);
                        sum += len;
                    }

                    byte[] bytes = new byte[sum];
                    for (int i = 0; i < sum; i++) {
                        bytes[i] = byteBuf.get(i);
                    }
//                    returnBytesHandler(bytes);
                } catch (IOException e) {
                }
        }
    }

    public static void main(String[] args) throws Exception {
        DSerialPort dSerialPort = new DSerialPort(1);
        dSerialPort.open();
    }
}
